package leakcanary

import java.io.File
import okio.ByteString.Companion.decodeHex
import shark.ByteArraySourceProvider
import shark.HeapTraversalInput
import shark.HprofHeapGraph.Companion.openHeapGraph
import shark.InitialState
import shark.ObjectGrowthDetector

class ObjectGrowthWarmupHeapDumper(
  private val objectGrowthDetector: ObjectGrowthDetector,
  private val delegate: HeapDumper,
  private val androidHeap: Boolean
) : HeapDumper {

  private var warm = false

  override fun dumpHeap(heapDumpFile: File) {
    if (!warm) {
      warmup()
      warm = true
    }
    delegate.dumpHeap(heapDumpFile)
  }

  private fun warmup() {
    val heapDumpsAsHex = listOf(
      { heapDump1Hex(androidHeap) },
      { heapDump2Hex(androidHeap) },
      { heapDump3Hex(androidHeap) }
    )
    var lastTraversalOutput: HeapTraversalInput = InitialState(1)
    for (heapDumpAsHex in heapDumpsAsHex) {
      lastTraversalOutput = ByteArraySourceProvider(
        heapDumpAsHex().decodeHex().toByteArray()
      ).openHeapGraph().use { heapGraph ->
        objectGrowthDetector.findGrowingObjects(heapGraph, lastTraversalOutput)
      }
    }
  }

  @SuppressWarnings("MaxLineLength")
  companion object {
    // Header:
    // 4a4156412050524f46494c4520312e302e33 is the header version string
    // then 00 is the string separator
    // 00000004 is the identifier byte size, 4 bytes
    // 0b501e7e ca55e77e (obsolete cassette) is a cool heap dump timestamp.
    internal fun heapDump1Hex(androidHeap: Boolean) = if (androidHeap) {
      "4a4156412050524f46494c4520312e302e3300000000040b501e7eca55e77e01000000000000001b000000016a6176612e6c616e672e7265662e5265666572656e63650200000000000000100000000100000002000000010000000101000000000000000c000000037265666572656e74010000000000000014000000046a6176612e6c616e672e4f626a656374020000000000000010000000010000000500000001000000040c000000000000006520000000050000000100000000000000000000000000000000000000000000000000000000000000000000050000000520000000020000000100000005000000000000000000000000000000000000000000000004000000000001000000030205000000022c000000000000000001000000000000001f000000066a6176612e6c616e672e7265662e5765616b5265666572656e6365020000000000000010000000010000000700000001000000060c00000000000000302000000007000000010000000200000000000000000000000000000000000000000000000400000000000005000000072c0000000000000000010000000000000021000000086c65616b63616e6172792e4b657965645765616b5265666572656e6365020000000000000010000000010000000900000001000000080100000000000000180000000a6865617044756d70557074696d654d696c6c69730100000000000000070000000b6b65790100000000000000080000000c6e616d650100000000000000150000000d7761746368557074696d654d696c6c69730100000000000000180000000e72657461696e6564557074696d654d696c6c69730c00000000000000512000000009000000010000000700000000000000000000000000000000000000000000001c000000010000000a0b000000000000753000040000000b020000000c020000000d0b0000000e0b05000000092c00000000000000000100000000000000140000000f6a6176612e6c616e672e537472696e670200000000000000100000000100000010000000010000000f0100000000000000090000001176616c756501000000000000000900000012636f756e740c000000000000008c200000001000000001000000050000000000000000000000000000000000000000000000080000000000020000001102000000120a05000000102300000013000000010000000105004d2100000014000000010000001000000008000000130000000123000000150000000100000001050049210000001600000001000000100000000800000015000000012c000000000000000001000000000000001400000017616e64726f69642e6f732e4275696c6402000000000000001000000001000000180000000100000017010000000000000010000000194d414e5546414354555245520100000000000000060000001a49440c000000000000004220000000180000000100000005000000000000000000000000000000000000000000000000000000020000001902000000140000001a0200000016000005000000182c000000000000000001000000000000001c0000001b616e64726f69642e6f732e4275696c642456455253494f4e020000000000000010000000010000001c000000010000001b01000000000000000b0000001d53444b5f494e540c000000000000004a200000001c0000000100000005000000000000000000000000000000000000000000000000000000010000001d0a0000002a0000050000001c210000001e0000000100000005000000002c00000000000000000100000000000000160000001f6a6176612e6c616e672e4f626a6563745b5d0200000000000000100000000100000020000000010000001f0c000000000000004520000000200000000100000005000000000000000000000000000000000000000000000000000000000000050000002022000000210000000100000001000000200000001e2c000000000000000001000000000000000a00000022486f6c64657202000000000000001000000001000000230000000100000022010000000000000008000000246c6973740c00000000000000392000000023000000010000000500000000000000000000000000000000000000000000000000000001000000240200000021000005000000232c0000000000000000"
    } else {
      "4a4156412050524f46494c4520312e302e3300000000040b501e7eca55e77e01000000000000001b000000016a6176612e6c616e672e7265662e5265666572656e63650200000000000000100000000100000002000000010000000101000000000000000c000000037265666572656e74010000000000000014000000046a6176612e6c616e672e4f626a656374020000000000000010000000010000000500000001000000040c000000000000006520000000050000000100000000000000000000000000000000000000000000000000000000000000000000050000000520000000020000000100000005000000000000000000000000000000000000000000000004000000000001000000030205000000022c000000000000000001000000000000001f000000066a6176612e6c616e672e7265662e5765616b5265666572656e6365020000000000000010000000010000000700000001000000060c00000000000000302000000007000000010000000200000000000000000000000000000000000000000000000400000000000005000000072c0000000000000000010000000000000021000000086c65616b63616e6172792e4b657965645765616b5265666572656e6365020000000000000010000000010000000900000001000000080100000000000000180000000a6865617044756d70557074696d654d696c6c69730100000000000000070000000b6b65790100000000000000080000000c6e616d650100000000000000150000000d7761746368557074696d654d696c6c69730100000000000000180000000e72657461696e6564557074696d654d696c6c69730c00000000000000622000000009000000010000000700000000000000000000000000000000000000000000001c000000010000000a0b000000000000753000040000000b020000000c020000000d0b0000000e0b0500000009210000000f0000000100000005000000002c0000000000000000010000000000000016000000106a6176612e6c616e672e4f626a6563745b5d020000000000000010000000010000001100000001000000100c000000000000004520000000110000000100000005000000000000000000000000000000000000000000000000000000000000050000001122000000120000000100000001000000110000000f2c000000000000000001000000000000000a00000013486f6c64657202000000000000001000000001000000140000000100000013010000000000000008000000156c6973740c00000000000000392000000014000000010000000500000000000000000000000000000000000000000000000000000001000000150200000012000005000000142c0000000000000000"
    }

    internal fun heapDump2Hex(androidHeap: Boolean) = if (androidHeap) {
      "4a4156412050524f46494c4520312e302e3300000000040b501e7eca55e77e01000000000000001b000000016a6176612e6c616e672e7265662e5265666572656e63650200000000000000100000000100000002000000010000000101000000000000000c000000037265666572656e74010000000000000014000000046a6176612e6c616e672e4f626a656374020000000000000010000000010000000500000001000000040c000000000000006520000000050000000100000000000000000000000000000000000000000000000000000000000000000000050000000520000000020000000100000005000000000000000000000000000000000000000000000004000000000001000000030205000000022c000000000000000001000000000000001f000000066a6176612e6c616e672e7265662e5765616b5265666572656e6365020000000000000010000000010000000700000001000000060c00000000000000302000000007000000010000000200000000000000000000000000000000000000000000000400000000000005000000072c0000000000000000010000000000000021000000086c65616b63616e6172792e4b657965645765616b5265666572656e6365020000000000000010000000010000000900000001000000080100000000000000180000000a6865617044756d70557074696d654d696c6c69730100000000000000070000000b6b65790100000000000000080000000c6e616d650100000000000000150000000d7761746368557074696d654d696c6c69730100000000000000180000000e72657461696e6564557074696d654d696c6c69730c00000000000000512000000009000000010000000700000000000000000000000000000000000000000000001c000000010000000a0b000000000000753000040000000b020000000c020000000d0b0000000e0b05000000092c00000000000000000100000000000000140000000f6a6176612e6c616e672e537472696e670200000000000000100000000100000010000000010000000f0100000000000000090000001176616c756501000000000000000900000012636f756e740c000000000000008c200000001000000001000000050000000000000000000000000000000000000000000000080000000000020000001102000000120a05000000102300000013000000010000000105004d2100000014000000010000001000000008000000130000000123000000150000000100000001050049210000001600000001000000100000000800000015000000012c000000000000000001000000000000001400000017616e64726f69642e6f732e4275696c6402000000000000001000000001000000180000000100000017010000000000000010000000194d414e5546414354555245520100000000000000060000001a49440c000000000000004220000000180000000100000005000000000000000000000000000000000000000000000000000000020000001902000000140000001a0200000016000005000000182c000000000000000001000000000000001c0000001b616e64726f69642e6f732e4275696c642456455253494f4e020000000000000010000000010000001c000000010000001b01000000000000000b0000001d53444b5f494e540c000000000000005b200000001c0000000100000005000000000000000000000000000000000000000000000000000000010000001d0a0000002a0000050000001c210000001e000000010000000500000000210000001f0000000100000005000000002c0000000000000000010000000000000016000000206a6176612e6c616e672e4f626a6563745b5d020000000000000010000000010000002100000001000000200c000000000000004920000000210000000100000005000000000000000000000000000000000000000000000000000000000000050000002122000000220000000100000002000000210000001e0000001f2c000000000000000001000000000000000a00000023486f6c64657202000000000000001000000001000000240000000100000023010000000000000008000000256c6973740c00000000000000392000000024000000010000000500000000000000000000000000000000000000000000000000000001000000250200000022000005000000242c0000000000000000"
    } else {
      "4a4156412050524f46494c4520312e302e3300000000040b501e7eca55e77e01000000000000001b000000016a6176612e6c616e672e7265662e5265666572656e63650200000000000000100000000100000002000000010000000101000000000000000c000000037265666572656e74010000000000000014000000046a6176612e6c616e672e4f626a656374020000000000000010000000010000000500000001000000040c000000000000006520000000050000000100000000000000000000000000000000000000000000000000000000000000000000050000000520000000020000000100000005000000000000000000000000000000000000000000000004000000000001000000030205000000022c000000000000000001000000000000001f000000066a6176612e6c616e672e7265662e5765616b5265666572656e6365020000000000000010000000010000000700000001000000060c00000000000000302000000007000000010000000200000000000000000000000000000000000000000000000400000000000005000000072c0000000000000000010000000000000021000000086c65616b63616e6172792e4b657965645765616b5265666572656e6365020000000000000010000000010000000900000001000000080100000000000000180000000a6865617044756d70557074696d654d696c6c69730100000000000000070000000b6b65790100000000000000080000000c6e616d650100000000000000150000000d7761746368557074696d654d696c6c69730100000000000000180000000e72657461696e6564557074696d654d696c6c69730c00000000000000732000000009000000010000000700000000000000000000000000000000000000000000001c000000010000000a0b000000000000753000040000000b020000000c020000000d0b0000000e0b0500000009210000000f00000001000000050000000021000000100000000100000005000000002c0000000000000000010000000000000016000000116a6176612e6c616e672e4f626a6563745b5d020000000000000010000000010000001200000001000000110c000000000000004920000000120000000100000005000000000000000000000000000000000000000000000000000000000000050000001222000000130000000100000002000000120000000f000000102c000000000000000001000000000000000a00000014486f6c64657202000000000000001000000001000000150000000100000014010000000000000008000000166c6973740c00000000000000392000000015000000010000000500000000000000000000000000000000000000000000000000000001000000160200000013000005000000152c0000000000000000"
    }

    internal fun heapDump3Hex(androidHeap: Boolean) = if (androidHeap) {
      "4a4156412050524f46494c4520312e302e3300000000040b501e7eca55e77e01000000000000001b000000016a6176612e6c616e672e7265662e5265666572656e63650200000000000000100000000100000002000000010000000101000000000000000c000000037265666572656e74010000000000000014000000046a6176612e6c616e672e4f626a656374020000000000000010000000010000000500000001000000040c000000000000006520000000050000000100000000000000000000000000000000000000000000000000000000000000000000050000000520000000020000000100000005000000000000000000000000000000000000000000000004000000000001000000030205000000022c000000000000000001000000000000001f000000066a6176612e6c616e672e7265662e5765616b5265666572656e6365020000000000000010000000010000000700000001000000060c00000000000000302000000007000000010000000200000000000000000000000000000000000000000000000400000000000005000000072c0000000000000000010000000000000021000000086c65616b63616e6172792e4b657965645765616b5265666572656e6365020000000000000010000000010000000900000001000000080100000000000000180000000a6865617044756d70557074696d654d696c6c69730100000000000000070000000b6b65790100000000000000080000000c6e616d650100000000000000150000000d7761746368557074696d654d696c6c69730100000000000000180000000e72657461696e6564557074696d654d696c6c69730c00000000000000512000000009000000010000000700000000000000000000000000000000000000000000001c000000010000000a0b000000000000753000040000000b020000000c020000000d0b0000000e0b05000000092c00000000000000000100000000000000140000000f6a6176612e6c616e672e537472696e670200000000000000100000000100000010000000010000000f0100000000000000090000001176616c756501000000000000000900000012636f756e740c000000000000008c200000001000000001000000050000000000000000000000000000000000000000000000080000000000020000001102000000120a05000000102300000013000000010000000105004d2100000014000000010000001000000008000000130000000123000000150000000100000001050049210000001600000001000000100000000800000015000000012c000000000000000001000000000000001400000017616e64726f69642e6f732e4275696c6402000000000000001000000001000000180000000100000017010000000000000010000000194d414e5546414354555245520100000000000000060000001a49440c000000000000004220000000180000000100000005000000000000000000000000000000000000000000000000000000020000001902000000140000001a0200000016000005000000182c000000000000000001000000000000001c0000001b616e64726f69642e6f732e4275696c642456455253494f4e020000000000000010000000010000001c000000010000001b01000000000000000b0000001d53444b5f494e540c000000000000006c200000001c0000000100000005000000000000000000000000000000000000000000000000000000010000001d0a0000002a0000050000001c210000001e000000010000000500000000210000001f00000001000000050000000021000000200000000100000005000000002c0000000000000000010000000000000016000000216a6176612e6c616e672e4f626a6563745b5d020000000000000010000000010000002200000001000000210c000000000000004d20000000220000000100000005000000000000000000000000000000000000000000000000000000000000050000002222000000230000000100000003000000220000001e0000001f000000202c000000000000000001000000000000000a00000024486f6c64657202000000000000001000000001000000250000000100000024010000000000000008000000266c6973740c00000000000000392000000025000000010000000500000000000000000000000000000000000000000000000000000001000000260200000023000005000000252c0000000000000000"
    } else {
      "4a4156412050524f46494c4520312e302e3300000000040b501e7eca55e77e01000000000000001b000000016a6176612e6c616e672e7265662e5265666572656e63650200000000000000100000000100000002000000010000000101000000000000000c000000037265666572656e74010000000000000014000000046a6176612e6c616e672e4f626a656374020000000000000010000000010000000500000001000000040c000000000000006520000000050000000100000000000000000000000000000000000000000000000000000000000000000000050000000520000000020000000100000005000000000000000000000000000000000000000000000004000000000001000000030205000000022c000000000000000001000000000000001f000000066a6176612e6c616e672e7265662e5765616b5265666572656e6365020000000000000010000000010000000700000001000000060c00000000000000302000000007000000010000000200000000000000000000000000000000000000000000000400000000000005000000072c0000000000000000010000000000000021000000086c65616b63616e6172792e4b657965645765616b5265666572656e6365020000000000000010000000010000000900000001000000080100000000000000180000000a6865617044756d70557074696d654d696c6c69730100000000000000070000000b6b65790100000000000000080000000c6e616d650100000000000000150000000d7761746368557074696d654d696c6c69730100000000000000180000000e72657461696e6564557074696d654d696c6c69730c00000000000000842000000009000000010000000700000000000000000000000000000000000000000000001c000000010000000a0b000000000000753000040000000b020000000c020000000d0b0000000e0b0500000009210000000f000000010000000500000000210000001000000001000000050000000021000000110000000100000005000000002c0000000000000000010000000000000016000000126a6176612e6c616e672e4f626a6563745b5d020000000000000010000000010000001300000001000000120c000000000000004d20000000130000000100000005000000000000000000000000000000000000000000000000000000000000050000001322000000140000000100000003000000130000000f00000010000000112c000000000000000001000000000000000a00000015486f6c64657202000000000000001000000001000000160000000100000015010000000000000008000000176c6973740c00000000000000392000000016000000010000000500000000000000000000000000000000000000000000000000000001000000170200000014000005000000162c0000000000000000"
    }
  }
}

fun HeapDumper.withDetectorWarmup(
  objectGrowthDetector: ObjectGrowthDetector,
  androidHeap: Boolean
): HeapDumper =
  ObjectGrowthWarmupHeapDumper(objectGrowthDetector, this, androidHeap)
